package net.example.lesson1;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zhuhai
 */
public class ReferenceDemo {
    private final static Logger log = LoggerFactory.getLogger(ReferenceDemo.class);

    /**
     * 强应用
     * 结论：垃圾回收不会回收指向强应用的对象,宁可OutOfMemoryError程序异常终止
     */
    public static void strongReferenceTest(){
        //在堆中创建一个对象obj,在栈中创建一个task来强引用此对象obj
        Task task = new Task("强引用任务");
        System.gc();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            log.error("异常："+e);
        }
        System.out.println("强应用测试："+task.getName());
    }
    /**
     * 软引用
     * 结论: 软引用只有系统在发生内存溢出异常，才把只被软引用的对象进行回收。且回收后不加入队列
     * 重写get方法
     * public T get() {
     *         T o = super.get();
     *         if (o != null && this.timestamp != clock)
     *             this.timestamp = clock;
     *         return o;
     *     }
     */
    public static void softReferenceTest() {
        //在堆中创建一个对象obj,在栈中创建一个task来强引用此对象obj
        Task task = new Task("软引用任务");
        //在栈中创建一个softReference来软引用此对象Obj 可以获取对象的属性值
        final ReferenceQueue<Task> queue = new ReferenceQueue<Task>();
        final SoftReference<Task> softReference = new SoftReference<Task>(task, queue);
        System.out.println("new Task ：" + task.getName());
        //软引用
        System.out.println("SoftReference Task:" + softReference.get().getName());
        System.out.println("ReferenceQueue Task:" + queue.poll());

        //断开task和Obj的强引用
        task = null;
        try {
            //此处空指针异常，说明断开引用
            System.out.println("断开task和Obj的强引用:" + task.getName());
        } catch (Exception e) {
            log.error("异常："+e);
        }
        //虽然断开了p和Obj的强引用,但是并没有被回收.
        System.out.println("断开強引用后 SoftReference Task:" + softReference.get().getName());
        System.out.println("断开強引用后 ReferenceQueue Task:" + queue.poll());
        //调用gc() 垃圾回收
        System.gc();
        // 运行结果  垃圾回收后 SoftReference 打印Task:软引用任務
        System.out.println("垃圾回收后 SoftReference Task:" + softReference.get().getName());
        System.out.println("垃圾回收后 ReferenceQueue Task:" + queue.poll());
        //设置启动內存1M 测试內存快溢出時候， finalized!!!!! 软引用进行垃圾回收
        //-Xms1m -Xmx1m
        try {
            final List<String> list = new ArrayList<>();
            int i=0;
            while(true){
                list.add("String" + i++);
            }
        }catch (Exception e) {
            log.error("异常："+e);
        }finally {
            //当溢出后，触发软引用垃圾回收 ，NullPointerException
            System.out.println("内存溢出 垃圾回收后 SoftReference Task:" + softReference.get().getName());
            System.out.println("内存溢出 ReferenceQueue Task:" + queue.poll());
        }
    }

    /**
     * 弱引用
     * 结论: 垃圾回收不论内存是否不足 都会回收只被弱引用关联的对象。
     * 指向Reference的get方法
     *  public T get() {
     *         return this.referent;
     *     }
     */
    public static void weakReferenceTest(){
        //在堆中创建一个对象Obj，栈中创建一个task来强引用此对象Obj
        Task task = new Task("弱引用任务");

        //在栈中创建一个weakReference来弱引用此对象Obj 可以获取对象的属性值
        final ReferenceQueue<Task> queue = new ReferenceQueue<Task>();
        final WeakReference<Task> weakReference=new WeakReference<Task>(task,queue);
        System.out.println("weakReference Task ：" + weakReference.get().getName());
        System.out.println("ReferenceQueue Task ：" + queue.poll());

        //断开task和Obj的强引用
        task = null;
        try {
            //此处空指针异常，说明断开引用
            System.out.println("断开task和Obj的强引用:" + task.getName());
        } catch (Exception e) {
            log.error("异常："+e);
        }

        System.out.println("断开task和Obj的强引用 weakReference Task ：" + weakReference.get().getName());

        System.gc();
        //垃圾回收后，若应用指向加入队列 此处打印java.lang.ref.WeakReference@37f8bb67
        System.out.println("System.gc ReferenceQueue Task ：" + queue.poll());
        //此处 空指針
        System.out.println("System.gc weakReference Task ：" + weakReference.get().getName());
    }

    /**
     * 虛引用
     * 结论：虛引用的作用就是能在这个对象被收集器回收时收到一个系统通知
     * public T get() {
     *         return null;
     *     }
     */
    public static void PhantomReferenceTest(){
        //在堆中创建一个对象Obj，栈中创建一个task来强引用此对象Obj
        final Task task=new Task("虛引用任务1");
        //引用隊列
        final ReferenceQueue<Task> referenceQueue = new ReferenceQueue<Task>();
        //在栈中创建一个虛引用来虚引用此对象Obj 不可以获取对象的属性值
        final PhantomReference<Task> phantomReference=new PhantomReference<Task>(task,referenceQueue);
        //打印报错 java.lang.NullPointerException
        System.out.println("phantomReference Task:" + phantomReference.get().getName());
    }
    /**
     * 虛引用
     */
    public static void PhantomReferenceTest1(){
        //在堆中创建一个对象Obj,栈中创建一个task来强引用此对象Obj
        Task task = new Task("虛引用任务2");
        //引用队列
        final ReferenceQueue<Task> referenceQueue = new ReferenceQueue<Task>();
        //在栈中创建一个虛引用来虚引用此对象Obj 不可以获取对象的属性值
        final PhantomReference<Task> phantomReference=new PhantomReference<Task>(task,referenceQueue);
        //打印输出: null
        // poll 查询队列中是否有元素.
        System.out.println("referenceQueue.poll:" + referenceQueue.poll());

        //断开task和obj的强引用
        task = null;
        // task被回收之后 队列referenceQueue中有值.
        System.gc();
        //查询虛引用是否有元素.
        System.out.println("System.gc() phantomReference.get():" + phantomReference.get());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            log.error("异常："+e);
        }
        //过 一秒钟之后再查询队列中是否有元素.
        System.out.println("System.gc() referenceQueue.poll():"+referenceQueue.poll());
    }

    public static void main(String[] args) {
        //强应用
//        strongReferenceTest();
        //软引用
//        softReferenceTest();
        //弱引用
//        weakReferenceTest();
        //虛引用_1
//        PhantomReferenceTest();
        //虛引用_2
        PhantomReferenceTest1();
    }
}

class Task{
    public Task(final String name) {
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize !!!!!");
    }
}
